假设想使用泛型代码来转发传递参数:

\begin{itemize}
\item 
可修改的对象应该在转发时，保持可修改的属性，以便它们可以修改。

\item 
常量对象应作为只读对象转发。

\item 
可移动的对象(可以“窃取”的对象，因为其即将销毁)应该作为可移动对象转发。
\end{itemize}

要在没有模板的情况下实现此功能，必须对这三种情况进行编程。例如，将f()的调用转发给函数g():

\hspace*{\fill} \\ %插入空行
\noindent
\textit{basics/move1.cpp}
\begin{lstlisting}[style=styleCXX]
#include <utility>
#include <iostream>

class X {
	...
};

void g (X&) {
	std::cout << "g() for variable\n";
}
void g (X const&) {
	std::cout << "g() for constant\n";
}
void g (X&&) {
	std::cout << "g() for movable object\n";
}
// let f() forward argument val to g():
void f (X& val) {
	g(val); // val is non-const lvalue => calls g(X&)
}
void f (X const& val) {
	g(val); // val is const lvalue => calls g(X const&)
}

void f (X&& val) {
	g(std::move(val)); // val is non-const lvalue => needs std::move() to call g(X&&)
}

int main()
{
	X v; // create variable
	X const c; // create constant
	
	f(v); // f() for nonconstant object calls f(X&) => calls g(X&)
	f(c); // f() for constant object calls f(X const&) => calls g(X const&)
	f(X()); // f() for temporary calls f(X&&) => calls g(X&&)
	f(std::move(v)); // f() for movable variable calls f(X&&) => calls g(X&&)
}
\end{lstlisting}

这里，f()将参数转发给g()的三种不同实现:

\begin{lstlisting}[style=styleCXX]
void f (X& val) {
	g(val); // val is non-const lvalue => calls g(X&)
}
void f (X const& val) {
	g(val); // val is const lvalue => calls g(X const&)
}
void f (X&& val) {
	g(std::move(val)); // val is non-const lvalue => needs std::move() to call g(X&&)
}
\end{lstlisting}

移动对象的代码(通过右值引用)与其他代码不同:需要\texttt{std::move()}，根据语言规则，移动语义无法传递。

\begin{tcolorbox}[colback=webgreen!5!white,colframe=webgreen!75!black]
\hspace*{0.75cm}移动语义不传递是有意为之的。若不是这样，就会在函数中第一次使用可移动对象时丢失其内部值。
\end{tcolorbox}

尽管在第三个f()中val声明为右值引用，但当用作表达式时，值类别是非常量左值(参见附录B)，并且在第一个f()中表现为val。若没有move()，将调用g(X\&)而不是g(X\&\&)来表示非常量左值。

想在通用代码中合并这三种情况，就要面对一个问题:

\begin{lstlisting}[style=styleCXX]
template<typename T>
void f (T& val) {
	g(val);
}
\end{lstlisting}

适用于前两种情况，但不适用于传递移动对象的(第三种)情况。

C++11为此引入了完善转发参数的特殊规则。代码如下所示:

\begin{lstlisting}[style=styleCXX]
template<typename T>
void f (T&& val) {
	g(std::forward<T>(val)); // perfect forward val to g()
}
\end{lstlisting}

std::move()没有模板参数，并且为传递的参数“触发”移动语义，而std::forward<>()会根据传递的模板参数“转发”移动语义。

不要认为模板参数T\&\&的行为就像特定类型T一样，它们应用完全不同的规则!但在语法上看起来一样:

\begin{itemize}
\item 
对于特定类型X\&\&声明一个参数为右值引用，只能绑定到一个可移动的对象(prvalue，比如临时对象；xvalue，比如通过std::move()传递的对象;详见附录B)。它总是可变的，所以可以“窃取”其值。

\begin{tcolorbox}[colback=webgreen!5!white,colframe=webgreen!75!black]
\hspace*{0.75cm}X const\&\&这样的类型合法，因为“窃取”可移动对象的内部表示需要修改该对象，但在实践中没有提供通用的语义。不过，也可以强制传递带有std::move()标记的临时变量或对象，但不能修改它们。
\end{tcolorbox}

\item 
模板参数T的T\&\&声明为转发引用(也称为通用引用)。

\begin{tcolorbox}[colback=webgreen!5!white,colframe=webgreen!75!black]
\hspace*{0.75cm}通用引用这个术语由Scott Meyers创造，其是一个常见的术语，可以指代“左值引用”或“右值引用”。因为“通用”太通用了，并且使用这种引用的主要原因是转发对象，所以C++17标准引入了术语转发引用，但它不会自动转发。这个术语没有描述它是什么，但描述了用于做什么。
\end{tcolorbox}

可以绑定到可变、不可变(即const)或可移动对象。在函数定义内部，参数可以是可变的、不可变的，也可以引用一个可以“窃取”内部函数的值。
\end{itemize}

注意，T必须是模板参数的名称。仅依赖模板参数是不够的，对于模板参数T，像typename T::iterator\&\&这样的声明只是一个右值引用，而不是转发引用。

因此，完善转发的整个程序如下所示:

\hspace*{\fill} \\ %插入空行
\noindent
\textit{basics/move2.cpp}
\begin{lstlisting}[style=styleCXX]
#include <utility>
#include <iostream>

class X {
	...
};

void g (X&) {
	std::cout << "g() for variable\n";
}
void g (X const&) {
	std::cout << "g() for constant\n";
}
void g (X&&) {
	std::cout << "g() for movable object\n";
}

// let f() perfect forward argument val to g():
template<typename T>
void f (T&& val) {
	g(std::forward<T>(val)); // call the right g() for any passed argument val
}

int main()
{
	X v; // create variable
	X const c; // create constant
	
	f(v); // f() for variable calls f(X&) => calls g(X&)
	f(c); // f() for constant calls f(X const&) => calls g(X const&)
	f(X()); // f() for temporary calls f(X&&) => calls g(X&&)
	f(std::move(v)); // f() for move-enabled variable calls f(X&&) => calls g(X&&)
}
\end{lstlisting}

当然，完美转发也可以与可变参数模板一起使用(参见4.3节的一些示例)。关于完美转发的详细介绍请参见15.6.3节。





